# -*- coding: utf-8 -*-
import py_global
import logging
from concurrent.futures import ThreadPoolExecutor
from time import time, sleep
from threading import Timer

from redis import Redis

from py_database.py_redis.py_redis import connection_pool

logger = logging.getLogger(__name__)


class DistributedLock(object):
    """分布式锁"""

    def __init__(self, name, expire: int = 10):
        self.name = name
        self.value = None
        self.expire = expire

    def __enter__(self):
        self.redis_client: Redis = Redis(connection_pool=connection_pool)
        while True:
            self.value = str(time())
            if self.redis_client.set(self.name, self.value, ex=self.expire, nx=True):
                return self.redis_client
            else:
                sleep(0.1)  # 延时0.1秒

    def __exit__(self, exc_type, exc_val, exc_tb):
        lua = """
            if redis.call('get', KEYS[1]) == ARGV[1] then 
                return redis.call('del', KEYS[1]) 
            else 
                return 0 
            end
        """
        # 实际调用register_script，为了匹配redis命令eval实现
        self.redis_client.eval(lua, 1, self.name, self.value)
        self.redis_client.close()


def renew(name, value, expire):
    """续约"""
    logger.info('renew start')
    redis_client: Redis = Redis(connection_pool=connection_pool)
    if redis_client.exists(name):
        result = redis_client.get(name)
        if result and result.decode() == value:  # 键存在，值相等
            redis_client.delete(name)
            if redis_client.set(name, value, ex=expire, nx=True):
                logger.info(f'before del after set success')
            else:
                logger.error(f'before del after set failed')
        else:
            logger.error(f'lock exists but not equal')
    else:
        if redis_client.set(name, value, ex=expire, nx=True):
            logger.info(f'lock not exists reset success')
    redis_client.close()


class ReentrantLock(object):
    """可重入锁"""

    def __init__(self, name, ):
        self.name = name
        self.value = None
        self.expire = 30  # 过期
        self.renewal = True  # 续期
        self.locked = False  # 是否获取到锁
        self.retry = 10  # 重试次数

    def __enter__(self):
        self.redis_client: Redis = Redis(connection_pool=connection_pool)
        times = 0
        while True:
            if times > self.retry:
                break
            self.value = str(time())
            args = (self.name, self.value, self.expire)
            if self.redis_client.set(self.name, self.value, ex=self.expire, nx=True):
                if self.renewal:
                    self.timer = Clock(interval=10, function=renew, args=args)
                    self.timer.start()

                self.locked = True
                break
            else:
                sleep(0.1)  # 延时0.1秒
                times += 1

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        lua = """
            if redis.call('get',KEYS[1]) == ARGV[1] then 
                return redis.call('del',KEYS[1]) 
            else 
                return 0 
            end
        """
        # 实际调用register_script，为了匹配redis命令eval实现
        result = self.redis_client.eval(lua, 1, self.name, self.value)
        if self.renewal:
            self.timer.cancel()
        self.redis_client.close()


def apply_function(name):
    # with DistributedLock('name-lock', expire=10):
    #     logger.info('lock locked')
    logger.info(f'run:{name}')
    with ReentrantLock(name):
        logger.info(f'locked:{name}')


def test_distributed_lock():
    with DistributedLock('name-lock', 10):
        logger.info('lock locked')

    size = 10
    executor = ThreadPoolExecutor(max_workers=10)
    for i in range(size * size):
        executor.submit(apply_function, args=())


def test_reentrant_lock():
    # with ReentrantLock('name-lock'):
    #     logger.info('lock locked')

    size = 10
    executor = ThreadPoolExecutor(max_workers=10)
    for i in range(size):
        args = (f'name-{i}',)
        executor.submit(apply_function, *args)

    sleep(10000)
    executor.shutdown()


class Clock(Timer):
    """
    Timer只执行一次，Clock可执行多次
    """

    def run(self):
        while not self.finished.is_set():
            self.finished.wait(self.interval)
            self.function(*self.args, **self.kwargs)


def test_clock():
    timer = Clock(interval=15, function=renew, args=('key', 'value', 10))
    timer.start()
    logger.info("start")

    sleep(60)
    timer.cancel()


if __name__ == '__main__':
    # redis_client: Redis = StrictRedis(host="127.0.0.1", port=6379, password="chench")
    # redis_client = Redis(connection_pool=connection_pool)

    # redis_cache(redis_client)
    # redis_string(redis_client)
    # redis_list(redis_client)

    # redis_client.close()

    # test_distributed_lock()
    # test_reentrant_lock()

    test_clock()

    pass
